﻿using System;
using System.Threading;

namespace PoolKit
{
    internal class Waitable<T>
        where T : class
    {
        private T _value;
        private readonly object _criticalSection = new object();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        public void Initialize(T value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            lock (_criticalSection)
            {
                if (_value != null)
                {
                    throw new InvalidOperationException("Value has already been set");
                }

                _value = value;

                Monitor.PulseAll(_criticalSection);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns>Value as it has been set</returns>
        public T WaitUntilValueIsSet()
        {
            lock (_criticalSection)
            {
                // Note: there seems to be no orphaned-waiter-that-will-not-get-pulsed case
                while (_value == null)
                {
                    Monitor.Wait(_criticalSection);
                }
            }

            return _value;
        }
    }
}